/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/

#include "stdafx.h"
#include <pgl/core/PGLArea2D.h>
#include <pgl/core/PGLArchive.h>
#include <pgl/core/PGLUtility.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


PGL_IMPLEMENT_DYNCREATE(CPGLArea2D, CPGLCoord2D);

CPGLArea2D::EAreaType CPGLArea2D::StringToAreaType(LPCTSTR szAreaType)
{
	CString str = szAreaType;
	str.MakeLower();

	if (str == _T("world"))
		return CoordWorld;
	else if (str == _T("normclip"))
		return CoordNormClip;
	else if (str == _T("screen"))
		return CoordBB;
	else 
		return CoordNorm;
}

CString CPGLArea2D::AreaTypeToString(EAreaType eAreaType)
{
	CString str;

	switch(eAreaType)
	{
	case CoordWorld:
		str=_T("world");
		break;
	case CoordNormClip: 
		str=_T("normclip");
		break;
	case CoordBB:
		str=_T("screen");
		break;
	default: 
		str=_T("norm");
		break;
	}

	return str;
}

CPGLArea2D::EAnchorType CPGLArea2D::StringToAnchorType(LPCTSTR szAnchorType)
{
	CString str = szAnchorType;
	str.MakeLower();

	if (str == _T("lowerright"))
		return AnchorLowerRight;
	else if (str == _T("upperleft"))
		return AnchorUpperLeft;
	else if (str == _T("upperright"))
		return AnchorUpperRight;
	else
		return AnchorLowerLeft;
}

CString CPGLArea2D::AnchorTypeToString(EAnchorType eAnchorType)
{
	CString str;

	switch (eAnchorType)
	{
	case AnchorLowerRight:
		str=_T("lowerright");
		break;
	case AnchorUpperLeft:
		str=_T("upperleft");
		break;
	case AnchorUpperRight:
		str=_T("upperright");
		break;
	case AnchorLowerLeft:
		str=_T("lowerleft");
		break;
	}
	
	return str;
}

void CPGLArea2D::SerializeXML(CPGLArchive& ar)
{
	static const TCHAR szTag[] = _T("CPGLArea2D");
	CString str;

	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());

	if(ar.IsStoring())
	{
		xml.AddChildElem(szTag);
		xml.IntoElem();

			// base class
			CPGLCoord2D::SerializeXML(ar);

			xml.AddChildElemEx(_T("Height"), m_dHeight);
			xml.AddChildElemEx(_T("Width"), m_dWidth);
			xml.AddChildElem(_T("Type"), AreaTypeToString(m_eType));
			xml.AddChildElem(_T("Anchor"), AnchorTypeToString(m_eAnchor));

		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;

		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// base class
			CPGLCoord2D::SerializeXML(ar);

			xml.FindGetChildData(_T("Height"),m_dHeight);
			xml.FindGetChildData(_T("Width"),m_dWidth);

			if(xml.FindGetChildData(_T("Type"),str))
			{
				m_eType=StringToAreaType(str);
			}

			if(xml.FindGetChildData(_T("Anchor"),str))
			{
				m_eAnchor=StringToAnchorType(str);
			}

		xml.OutOfElem();
	}
}

#ifdef _DEBUG
void CPGLArea2D::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLCoord2D::Dump( dc );
}
void CPGLArea2D::AssertValid() const
{
    // call inherited AssertValid first
    CPGLCoord2D::AssertValid();

} 
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CPGLArea2D::CPGLArea2D(double _x, double _y, double _width, double _height, ECoord2DType _posType, EAreaType _type, EAnchorType _anchor)
: CPGLCoord2D(_x,_y,_posType)
{
	m_eAnchor = _anchor;

	SetArea(_width,_height,_type);
}

CPGLArea2D& CPGLArea2D::operator = (const CPGLArea2D& a)
{
	if (&a==this)
	{
		this->CPGLCoord2D::operator =(a);

		m_dHeight = a.m_dHeight;
		m_dWidth = a.m_dWidth;
		m_eAnchor = a.m_eAnchor;
		m_eType = a.m_eType;
	}
	return *this;
}


CPGLArea2D::~CPGLArea2D()
{

}

void CPGLArea2D::SetArea(double _width, double _height, EAreaType _type )
{
	m_eType = _type;

	m_dWidth = _width;
	m_dHeight = _height;
}

void CPGLArea2D::SetArea(const CPGLRectD& rBBox, EAreaType _type)
{
	m_eType = _type;

	CPGLCoord2D::ECoord2DType eCoordType;
	gfxinterface::SPointD point;

	switch (m_eType)
	{
	case CoordWorld:
		eCoordType = CPGLCoord2D::Coord2DWorld;
		break;
	case CoordNorm:
		eCoordType = CPGLCoord2D::Coord2DNorm;
		break;
	case CoordNormClip:
		eCoordType = CPGLCoord2D::Coord2DNormClip;
		break;
	case CoordBB:
		eCoordType = CPGLCoord2D::Coord2DBB;
		break;
	default:
		ASSERT(FALSE);
	}

	switch(m_eAnchor)
	{
	case AnchorLowerLeft:
		point.x=rBBox.left;
		point.y=rBBox.bottom;
		break;
	case AnchorLowerRight:
		point.x=rBBox.right;
		point.y=rBBox.bottom;
		break;
	case AnchorUpperLeft:
		point.x=rBBox.left;
		point.y=rBBox.top;
		break;
	case AnchorUpperRight:
		point.x=rBBox.right;
		point.y=rBBox.top;
		break;
	default:
		ASSERT(FALSE);
	}

	ClearTrans();
	SetPosition(point, eCoordType);

	m_dWidth = rBBox.GetWidth();
	m_dHeight = rBBox.GetHeight();
}

gfxinterface::SPointD CPGLArea2D::GetScreenSize(const gfxinterface::CGfxInterface& gfx) const
{
	gfxinterface::SPointD point;

	switch(m_eType)
	{
	case CoordWorld:
		point.x=gfx.WorldToBBAbsx(m_dWidth);
		point.y=fabs(gfx.WorldToBBAbsy(m_dHeight));
		break;
	case CoordNorm:
		point.x=gfx.NormBBoxToBBAbsx(m_dWidth);
		point.y=fabs(gfx.NormBBoxToBBAbsy(m_dHeight));
		break;
	case CoordNormClip:
		point.x=gfx.NormClipBBoxToBBAbsx(m_dWidth);
		point.y=fabs(gfx.NormClipBBoxToBBAbsy(m_dHeight));
		break;
	case CoordBB:
		point.x=m_dWidth;
		point.y=m_dHeight;
		break;
	default:
		ASSERT(FALSE);
	}

	return point;
}

gfxinterface::SPointD CPGLArea2D::GetWorldSize(const gfxinterface::CGfxInterface& gfx) const
{
	gfxinterface::SPointD point;

	switch(m_eType)
	{
	case CoordWorld:
		point.x=m_dWidth;
		point.y=m_dHeight;
		break;
	case CoordNorm:
		point.x=gfx.NormBBoxToWorldAbsx(m_dWidth);
		point.y=gfx.NormBBoxToWorldAbsy(m_dHeight);
		break;
	case CoordNormClip:
		point.x=gfx.NormClipBBoxToWorldAbsx(m_dWidth);
		point.y=gfx.NormClipBBoxToWorldAbsy(m_dHeight);
		break;
	case CoordBB:
		point.x=gfx.BBToWorldAbsx(m_dWidth);
		point.y=gfx.BBToWorldAbsy(m_dHeight);
		break;
	default:
		ASSERT(FALSE);
	}

	return point;
}

CPGLRectD CPGLArea2D::GetWorldBBox(const gfxinterface::CGfxInterface &gfx) const
{
	gfxinterface::SPointD point = GetWorldPosition(gfx);
	gfxinterface::SPointD size = GetWorldSize(gfx);

	return GetWorldRectFromAnchor(point, size);
}

CPGLRectD CPGLArea2D::GetScreenBBox(const gfxinterface::CGfxInterface& gfx) const
{
	gfxinterface::SPointD point = GetScreenPosition(gfx);
	gfxinterface::SPointD size = GetScreenSize(gfx);

//	size.y*=gfx.GetYDirectionUp();

	return GetScreenRectFromAnchor(gfx, point, size);
}

CPGLRectD CPGLArea2D::GetWorldRectFromAnchor(const gfxinterface::SPointD& point, const gfxinterface::SPointD& size) const
{
	CPGLRectD rect;

	switch(m_eAnchor)
	{
	case AnchorLowerLeft:
		rect.left = point.x;
		rect.bottom = point.y;
		rect.right = point.x + size.x;
		rect.top = point.y + size.y;
		break;
	case AnchorLowerRight:
		rect.left = point.x - size.x;
		rect.bottom = point.y;
		rect.right = point.x;
		rect.top = point.y + size.y;
		break;
	case AnchorUpperLeft:
		rect.left = point.x;
		rect.bottom = point.y - size.y;
		rect.right = point.x + size.x;
		rect.top = point.y;
		break;
	case AnchorUpperRight:
		rect.left = point.x - size.x;
		rect.bottom = point.y- size.y;
		rect.right = point.x;
		rect.top = point.y;
		break;
	default:
		ASSERT(FALSE);
	}

	return rect;
}

CPGLRectD CPGLArea2D::GetScreenRectFromAnchor(const gfxinterface::CGfxInterface& gfx, const gfxinterface::SPointD& point, const gfxinterface::SPointD& size) const
{
	CPGLRectD rect;

	switch(m_eAnchor)
	{
	case AnchorLowerLeft:
		rect.left = point.x;
		rect.bottom = point.y;
		rect.right = point.x + size.x;
		rect.top = point.y+ gfx.GetYDirectionUp()*size.y;
		break;
	case AnchorLowerRight:
		rect.left = point.x - size.x;
		rect.bottom = point.y;
		rect.right = point.x;
		rect.top = point.y + gfx.GetYDirectionUp()* size.y;
		break;
	case AnchorUpperLeft:
		rect.left = point.x;
		rect.bottom = point.y-gfx.GetYDirectionUp()*size.y;
		rect.right = point.x + size.x;
		rect.top = point.y;
		break;
	case AnchorUpperRight:
		rect.left = point.x - size.x;
		rect.bottom = point.y-gfx.GetYDirectionUp()*size.y;
		rect.right = point.x;
		rect.top = point.y;
		break;
	default:
		ASSERT(FALSE);
	}

	return rect;
}

CPGLRectD CPGLArea2D::GetBBox() const
{
	gfxinterface::SPointD point = GetPosition();
	CPGLRectD rect;

	switch(m_eAnchor)
	{
	case AnchorLowerLeft:
		rect.left=point.x;
		rect.bottom=point.y;
		rect.right=rect.left+m_dWidth;
		rect.top=rect.bottom+m_dHeight;
		break;
	case AnchorLowerRight:
		rect.right=point.x;
		rect.bottom=point.y;
		rect.left=rect.right-m_dWidth;
		rect.top=rect.bottom+m_dHeight;
		break;
	case AnchorUpperLeft:
		rect.left=point.x;
		rect.top=point.y;
		rect.right=rect.left+m_dWidth;
		rect.bottom=rect.top-m_dHeight;
		break;
	case AnchorUpperRight:
		rect.right=point.x;
		rect.top=point.y;
		rect.left=rect.right-m_dWidth;
		rect.bottom=rect.top-m_dHeight;
		break;
	}

	return rect;
}

void CPGLArea2D::Resize(gfxinterface::CGfxInterface &gfx, double dWidth, double dHeight, EAreaType type)
{
	switch (m_eType)
	{
	// Normalized case
	case CoordNorm:
			// types
			switch(type)
			{
			case CoordNorm:
				m_dWidth=dWidth;
				m_dHeight=dHeight;
				break;
			case CoordNormClip:
				PGL_TRACE(_T("A IMPLEMENTER !!!!"));
				m_dWidth=dWidth;
				m_dHeight=dHeight;
				break;
			case CoordWorld:
				m_dWidth=gfx.WorldToNormAbsx(dWidth);
				m_dHeight=gfx.WorldToNormAbsy(dHeight);
				break;
			case CoordBB:
				m_dWidth=gfx.BBToNormAbsx(dWidth);
				m_dHeight=gfx.BBToNormAbsy(dHeight);
				break;
			}
		break;

	// Normalized case, clip box
	case CoordNormClip:
			// types
			switch(type)
			{
			case CoordNorm:
				PGL_TRACE(_T("A IMPLEMENTER !!!!"));
				m_dWidth=dWidth;
				m_dHeight=dHeight;
				break;
			case CoordNormClip:
				m_dWidth=dWidth;
				m_dHeight=dHeight;
				break;
			case CoordWorld:
				m_dWidth=gfx.WorldToNormClipAbsx(dWidth);
				m_dHeight=gfx.WorldToNormClipAbsy(dHeight);
				break;
			case CoordBB:
				m_dWidth=gfx.BBToNormClipAbsx(dWidth);
				m_dHeight=gfx.BBToNormClipAbsy(dHeight);
				break;
			}
		break;

	// World case
	case CoordWorld:
			// types
			switch(type)
			{
			case CoordNorm:
				m_dWidth=gfx.NormBBoxToWorldAbsx(dWidth);
				m_dHeight=gfx.NormBBoxToWorldAbsy(dHeight);
				break;
			case CoordNormClip:
				m_dWidth=gfx.NormClipBBoxToWorldAbsx(dWidth);
				m_dHeight=gfx.NormClipBBoxToWorldAbsy(dHeight);
				break;
			case CoordWorld:
				m_dWidth=dWidth;
				m_dHeight=dHeight;
				break;
			case CoordBB:
				m_dWidth=gfx.BBToWorldAbsx(dWidth);
				m_dHeight=gfx.BBToWorldAbsy(dHeight);
				break;
			}
			break;
		break;

	// BB case
	case CoordBB:
			// types
			switch(type)
			{
			case CoordNorm:
				m_dWidth=gfx.NormBBoxToBBAbsx(dWidth);
				m_dHeight=gfx.NormBBoxToBBAbsy(dHeight);
				break;
			case CoordNormClip:
				m_dWidth=gfx.NormClipBBoxToBBAbsx(dWidth);
				m_dHeight=gfx.NormClipBBoxToBBAbsy(dHeight);
				break;
			case CoordWorld:
				m_dWidth=gfx.WorldToBBAbsx(dWidth);
				m_dHeight=gfx.WorldToBBAbsy(dHeight);
				break;
			case CoordBB:
				m_dWidth=dWidth;
				m_dHeight=dHeight;
				break;
			}
			break;
		break;
	}
}
